<?php

namespace Modules\Production\Repository;


use App\Services\InventoryService;
use App\Services\KingDeeService;
use App\Services\UnitService;
use Illuminate\Support\Facades\DB;
use Modules\Base\Repository\MaterialRepository;
use Modules\Inventory\Repository\InventoryRepository;
use Modules\Inventory\Repository\InventoryTransRepository;
use Modules\Production\Models\ProductionOrder;
use Modules\Production\Models\ProductionOrderDetail;
use Modules\Production\Models\ProductionReportOrder;
use Modules\Production\Models\ProductionWareDetail;
use Modules\Production\Models\ProductionWareOrder;
use App\Enums\InvTransDocType;
use Modules\Production\Service\TaskScanService;

class productionWareOrderRepository
{
    /**
     * 生产入库单据编号生成
     * @return string
     */
    static public function makeDocno(){
        return 'SCRK' . date('YmdHis') . rand(100,999);
    }

    /**
     * 生产入库扫描数量确认
     * @param $postData
     */
    static public function qtyConfirm($postData){

        $reportInfo = ProductionReportOrderRepository::getReportOrderAndDetailById($postData['report_id']);

        if($postData['qty'] > $reportInfo['detail'][0]['qty']){
            throw new \Exception('数量不能大于汇报数');
        }

        if($reportInfo['detail'][0]['inbound_qty'] >= $reportInfo['detail'][0]['qty']){
            throw new \Exception('汇报单已全部入库');
        }

        try{

            DB::beginTransaction();

            $wareOrder = self::getWareOrderByReportId($reportInfo->id);
            if($wareOrder){

                $wareOrderId = $wareOrder->id;

                //数量校验，总扫描数不能大于汇报数
                $wareDetailSum = ProductionWareDetail::where('order_id',$wareOrder->id)->sum('qty');
                if( ($wareDetailSum + $postData['qty']) > $reportInfo['detail'][0]['qty']){
                    throw new \Exception('数量不能大于汇报数');
                }

                //已扫描过
                $wareDetail = ProductionWareDetail::where([
                    ['order_id',$wareOrder->id],
                    ['material_no',$postData['material_no']],
                    ['lot_num',empty($postData['to_lot_num']) ? $postData['lot_num'] : $postData['to_lot_num']],
                    ['location',$postData['location']],
                ])->first();
                if($wareDetail){
                    //存在同箱号同库位明细，累加
                    $wareDetail->increment('qty',$postData['qty']);
                }else{
                    //不存在新增
                    //保存生产入库单据体
                    (new ProductionWareDetail())->storeBy([
                        'order_id'      => $wareOrder->id,
                        'material_no'   => $postData['material_no'],
                        'qty'           => $postData['qty'],
                        'lot_num'       => empty($postData['to_lot_num']) ? $postData['lot_num'] : $postData['to_lot_num'],
                        'source_lot_num'=> $postData['lot_num'],
                        'location'      => $postData['location'],
                    ]);
                }

            }else{
                //第一次扫描

                //保存生产入库单据头
                $wareOrderId = (new ProductionWareOrder)->storeBy([
                    'docno' => UnitService::createDocno('SCRK'),
                    'report_order_id' => $reportInfo->id,
                    'report_no' => $reportInfo->docno,
                ]);

                //保存生产入库单据体
                (new ProductionWareDetail())->storeBy([
                    'order_id'      => $wareOrderId,
                    'material_no'   => $postData['material_no'],
                    'qty'           => $postData['qty'],
                    'lot_num'       => empty($postData['to_lot_num']) ? $postData['lot_num'] : $postData['to_lot_num'],
                    'source_lot_num'=> $postData['lot_num'],
                    'location'      => $postData['location'],
                ]);

            }

            DB::commit();

            return ['ware_id' => $wareOrderId];

        }catch (\Exception $e){
            DB::rollBack();
            throw $e;
        }

    }

    /**
     * 创建生产入库单信息
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    static public function saveWareOrder($data){

        if(count(array_unique(array_column($data,'report_detail_id'))) != 1){
            throw new \Exception('明细项必须为同一汇报单');
        }

        $qtySum = array_sum(array_column($data,'qty'));

        //汇报明细和单据头
        $item = ProductionReportOrderRepository::getReportDetailAndOrderById($data[0]['report_detail_id']);

        if($item->inbound_qty >= $item->qty){
            throw new \Exception('当前汇报单数量已全部入库，不能重复入库');
        }

        if( ($qtySum + $item->inbound_qty) > $item->qty){
            throw new \Exception('入库扫描总数量不能大于汇报单');
        }

        if($qtySum != $item->qty){
            throw new \Exception('明细总数量必须等于汇报数量');
        }

        //保存生产入库单据头
        $wareOrderId = (new ProductionWareOrder)->storeBy([
            'org_code'      => $item->order->org_code,
            'cargo_type'    => $item->order->cargo_type,
            'cargo_code'    => $item->order->cargo_code,
            'docno'         => UnitService::createDocno('SCRK'),
            'report_order_id' => 0,
            'report_no'     => '',
            'kingdee_status'=> 1
        ]);

        $orderItem = [];
        foreach ($data as $val){

            //回写汇报单的入库数量
            ProductionReportOrderRepository::rewriteInboundQty($item->order->id,$item->material_no,$val['qty']);

            $orderItem[] = [
                'order_id'      => $wareOrderId,
                'report_id'     => $item->order->id,
                'report_no'     => $item->order->docno,
                'material_no'   => $item->material_no,
                'qty'           => $val['qty'],
                'lot_num'       => $val['lot_num'],
                'wms_code'      => TaskScanService::getWmsCodeByLocation($val['location']),
                'source_lot_num'=> '',
                'location'      => $val['location'],
                'creator_id'    => UnitService::getUserId(),
                'created_at'    => time(),
                'updated_at'    => time(),
            ];

        }

        ProductionWareDetail::insert($orderItem);

        return $wareOrderId;

    }

    /**
     * 生产入库扫描提交
     * @param $postData
     * @return bool
     * @throws \Throwable
     */
    static public function qtySubmit($postData){

        try {

            DB::beginTransaction();

            //保存生产入库单据
            $wareOrderId = self::saveWareOrder($postData['items']);

            //生产入库单信息
            $wareOrder = self::getWareOrderById($wareOrderId,true);
            /*if($wareOrder->kingdee_status != 0){
                throw new \Exception('当前汇报单已提交');
            }*/

            foreach ($wareOrder->detail as $item){

                //产品入库
                InventoryService::addInv([
                    'org_code'      => $wareOrder->org_code,
                    'cargo_code'    => $wareOrder->cargo_code,
                    'supplier_code' => $wareOrder->docno,
                    'doc_id'        => $wareOrder->id,
                    'docno'         => $wareOrder->docno,
                    'doc_type'      => InvTransDocType::PRODUCTION_RECEIPT->value,
                    'material_no'   => $item->material_no,
                    'location'      => $item->location,
                    'qty'           => $item->qty,
                    'unit'          => MaterialRepository::getUnitByMaterial($item->material_no),
                    'lotnum'        => $item->lot_num,
                ]);

            }

            self::pushKingDee($wareOrder,$postData['time']);

            DB::commit();

            return true;

        }catch (\Exception $e){
            DB::rollBack();
            throw $e;
        }

    }

    /**
     * 生产汇报单推送金蝶
     * @param $order
     */
    static public function pushKingDee($wareOrder,$times = null){
        $order = [
            'FBILLTYPEID'   => 'PRD_INSTOCK',
            'FBILLNO'       => $wareOrder->docno,
            'FORGID'        => $wareOrder->org_code,
            'FDATE'         => $times ?: date('Y-m-d H:i:s'),
        ];

        $order['FEntity'] = [];

        $kingdee = self::geWareDetailGroupBy($wareOrder->id);

        foreach ($kingdee as $val){

            $row = [
                'FOwnerTypeId'          => $wareOrder->cargo_type,
                'FOwnerId'              => $wareOrder->cargo_code,
                'FSRCBILLNO'            => $val->report_no,
                'FMATERIALID'           => $val->material_no,
                'FUNITID'               => MaterialRepository::getUnitByMaterial($val->material_no),
                'FQTY'                  => $val->qty,
                'FSTOCKID'              => $val->wms_code,
                //'FDESTSTOCKID' => '仓库（传编码）',
                'FGIVEAWAY'             => 'false',
            ];

            $order['FEntity'][] = $row;
        }

        return (new KingDeeService())->form('TVJB_DJWMSDJ')->invokeSave($order);
    }

    /**
     * 根据生产汇报单id获取生产入库单
     * @param $reportId
     */
    static public function getWareOrderByReportId($reportId,$extDetail = false){

        $build = ProductionWareOrder::where('report_order_id',$reportId);
        if($extDetail){
            $build = $build->with('detail');
        }
        return $build->first();
    }

    /**
     * 根据生产入库单id获取订单和详情
     * @param $reportId
     */
    static public function getWareOrderById($wareId,$extDetail = false){

        $build = ProductionWareOrder::where('id',$wareId);
        if($extDetail){
            $build = $build->with('detail');
        }
        return $build->first();
    }

    /**
     * 获取生产入库明细汇总信息
     * @param $wareId
     * @return mixed
     */
    static public function geWareDetailGroupBy($wareId){
        return ProductionWareDetail::select('report_no','material_no','wms_code',DB::raw('sum(qty) as qty'))
            ->where('order_id',$wareId)
            ->groupBy('report_no','material_no','wms_code')
            ->get();
    }

    /**
     * 根据汇报单id查询入库明细
     * @param $reportId
     * @return mixed
     */
    static public function getDetailWithOrderByReportId($reportId){
        return ProductionWareDetail::where('report_id',$reportId)->with('order')->first();
    }

    /**
     * 删除生产入库单据
     * @param $wareId
     * @return mixed
     */
    static public function deleteById($wareId){
        return ProductionWareOrder::where('id',$wareId)->update([
            'deletor_id' => UnitService::getUserId(),
            'deleted_at' => time()
        ]);
    }

    /**
     * 生成入库删除逻辑处理
     * @param $id
     */
    static public function deleteBy($id){

        $wareOrder = self::getWareOrderById($id,true);

        if(!$wareOrder){
            throw new \Exception('入库单不存在');
        }

        try {
            DB::beginTransaction();
            //生产入库单删除
            self::deleteById($wareOrder->id);
            //删除库存变动记录
            InventoryTransRepository::deleteByDocId($wareOrder->id);

            $inboundCount = 0;
            foreach ($wareOrder->detail as $detailItem){
                //即时库存
                $nowInv = InventoryRepository::getInvByLocationAndLotnum(
                    $wareOrder->org_code,
                    $wareOrder->cargo_code,
                    $detailItem->material_no,
                    $detailItem->location,
                    $detailItem->lot_num
                );

                //重算库存
                $qty = InventoryRepository::resetInventory($wareOrder->org_code,
                    $wareOrder->cargo_code,
                    $detailItem->material_no,
                    $detailItem->lot_num,
                    $detailItem->location
                );
                $nowInv->update(['qty' => $qty]);

                $inboundCount += $detailItem->qty;
            }

            //释放生产汇报入库数量
            ProductionReportOrderRepository::decInboundQtyByReportId($wareOrder->detail[0]->report_id,$inboundCount);

            $kingdee = new KingDeeService();
            //反审金蝶生产入库单据
            $kingdee->form('PRD_INSTOCK')->unAudit($wareOrder->docno);
            //删除金蝶生产入库单据
            $kingdee->delete($wareOrder->docno);
            DB::commit();
            return true;
        }catch (\Exception $e){
            DB::rollBack();
            throw $e;
        }

    }


}
